﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Configuration;
using uTorrentRemote.Net;
using System.Collections.Specialized;
using System.Threading;
using System.IO;

namespace uTorrentRemote.TokenDispatchers
{
    public class RuTrackerDownloadDispatcher : ITokenDispatcher
    {
        private ILog log = null;

        public RuTrackerDownloadDispatcher(ILog log)
        {
            this.log = log;
        }

        private bool isInitiated = false;

        private void init()
        {
            Thread thread = new Thread(new ThreadStart(worker));
            thread.Start();

            isInitiated = true;
        }

        // dictionary:
        // url | flag
        // where
        // url  - url to be loaded
        // flag - shows if url has been already loaded
 
        private Dictionary<string, bool> urls = new Dictionary<string, bool>();

        public bool TryToken(Token token)
        {
            string url = token.ToString();

            Regex regex = new Regex(@"http\://rutracker\.org/forum/viewtopic\.php\?t=\d+", RegexOptions.IgnoreCase);
            
            if (!regex.Match(url).Success)
                return false;

            if (!isInitiated)
                init();

            if (urls.ContainsKey(url))
                return false;
            else
            {
                urls[url] = false; // add url with 'not loaded' flag
                log.Write("Added " + url);
            }

            // at this place we have working thread to download urls and update flags

            return true;
        }

        WebClientEx client = null;
        private void worker()
        {
            relogin();

            while(2 + 2 == 4)
            {
                foreach (string url in urls.Keys.ToList()) // "ToList()" is to avoid "Collection was modified; enumeration operation may not execute." exception
                {
                    if (urls[url] == false)
                    {
                        // if we can't download a file, may be it's login issue?
                        if (!tryUrl(url, 3))
                        {
                            log.Write("3 unsuccessful attempts, waiting for 3 minutes, relogin and try again");
                            Thread.Sleep(3 * 60 * 1000);
                            relogin(); // try to relogin
                            tryUrl(url, 2);
                        }
                        urls[url] = true;
                    }
                }

                Thread.Sleep(5 * 1000);
            }
        }

        private bool tryUrl(string url, int maxTries)
        {
            int cnt = maxTries;
            bool success = false;
            do
            {
                cnt--;

                try
                {
                    log.Write("Trying to download from " + url);
                
                    var bytes_response = client.DownloadData(url);
                    string response = Encoding.UTF8.GetString(bytes_response);

                    Regex regex = new Regex("\"(?<download_link>http\\://dl\\.rutracker\\.org/forum/dl\\.php\\?t=\\d\\d+)\"");
                    Match m = regex.Match(response);
                    if (m.Success)
                    {
                        string download_link = m.Groups["download_link"].Value;
                        downloadToFile(download_link, url);
                        success = true;
                    }
                    else
                    {
                        log.Write("torrent download link not found");
                    }
                }
                catch(Exception ex)
                {
                    log.Write("Exception: " + ex.Message);
                }
                if(!success)
                    Thread.Sleep(60 * 1000);

            }while (!success && cnt > 0);
            return success;
        }
        
        private void downloadToFile(string url, string referer)
        {
            client.Headers.Add("Referer", referer);
            var response = client.UploadValues(url, new NameValueCollection { });
            client.Headers.Remove("Referer");

            string directory = ConfigurationManager.AppSettings["store_directory"];
            
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            DateTime now = DateTime.Now;
            string filename = String.Format("{0:D4}-{1:D2}-{2:D2}_{3:D2}-{4:D2}-{5:D2}_{6}.torrent", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond);
            string path = Path.Combine(directory, filename);
            
            File.WriteAllBytes(path, response);

            log.Write("Torrent file downloaded from " + url + " to " + path);
        }

        private void relogin()
        {
            bool loggedIn = false;

            do
            {
                try
                {
                    client = new WebClientEx();
                    var data = new NameValueCollection
                    {
                        { "login_username", ConfigurationManager.AppSettings["rutracker_login"] },
                        { "login_password", ConfigurationManager.AppSettings["rutracker_password"] },
                        { "login", "%C2%F5%EE%E4" }
                    };

                    var response1 = client.UploadValues("http://login.rutracker.org/forum/login.php", data);
                    string str = Encoding.UTF8.GetString(response1).ToLower();
                    string logged_in_indicator = String.Format("<b class=\"med\">{0}</b>", ConfigurationManager.AppSettings["rutracker_login"]).ToLower();
                    if (str.Contains(logged_in_indicator))
                    {
                        log.Write("OK Logged in to rutracker.org");
                        loggedIn = true;
                    }
                    else
                    {
                        log.Write("ERROR Can't log in to rutracker.org (will keep on trying)");
                        Thread.Sleep(60 * 1000); // wait for 1 minute
                    }
                }
                catch (Exception ex)
                {
                    log.Write("Exception: " + ex.Message);
                    Thread.Sleep(60 * 1000);
                }
            } while (!loggedIn);
        }
    }
}
